#ifndef XG_SEMAPHORE_CPP
#define XG_SEMAPHORE_CPP
////////////////////////////////////////////////////////
#include "../Semaphore.h"

Semaphore::Semaphore()
{
	created = false;
	handle = INVALID_HANDLE_VALUE;
}
Semaphore::~Semaphore()
{
	close();
}
bool Semaphore::canUse() const
{
	return HandleCanUse(handle);
}
bool Semaphore::open(HANDLE handle)
{
	CHECK_FALSE_RETURN(HandleCanUse(handle));
	
	close();
	
	this->handle = handle;
	this->created = false;

	return true;
}


#ifdef XG_LINUX

#include <sys/ipc.h>
#include <sys/sem.h>


void Semaphore::close()
{
	if (canUse() && created) semctl(handle, 0, IPC_RMID);

	handle = INVALID_HANDLE_VALUE;
	created = false;
}
bool Semaphore::wait() const
{
	CHECK_FALSE_RETURN(canUse());

	struct sembuf sem_b;

	sem_b.sem_op = -1;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;
	
	return semop(handle, &sem_b, 1) >= 0;
}
bool Semaphore::release() const
{
	CHECK_FALSE_RETURN(canUse());

	struct sembuf sem_b;

	sem_b.sem_op = 1;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;
	
	return semop(handle, &sem_b, 1) >= 0;
}
bool Semaphore::open(const string& name)
{
	if (name.empty()) return false;

	key_t key = ftok(name.c_str(), 0);

	if (key < 0)
	{
		CHECK_FALSE_RETURN(errno == 0 || errno == ENOENT || errno == ENOTDIR);

		key = (key_t)(MD5GetUINT32(name.c_str(), name.length()) % 0xFFFF);
	}

	return open(semget(key, 0, IPC_EXCL | XG_IPC_FLAG)); 
}
bool Semaphore::create(const string& name, int size)
{
	if (name.empty()) return false;

	key_t key = ftok(name.c_str(), 0);

	if (key < 0)
	{
		CHECK_FALSE_RETURN(errno == 0 || errno == ENOENT || errno == ENOTDIR);

		key = (key_t)(MD5GetUINT32(name.c_str(), name.length()) % 0xFFFF);
	}

	return created = open(semget(key, size, IPC_CREAT | IPC_EXCL | XG_IPC_FLAG));
}

#else

void Semaphore::close()
{
	if (canUse()) CloseHandle(handle);

	handle = INVALID_HANDLE_VALUE;
	created = true;
}
bool Semaphore::wait() const
{
	CHECK_FALSE_RETURN(canUse());

	return WaitForSingleObject(handle, INFINITE) != WAIT_FAILED;
}
bool Semaphore::release() const
{
	CHECK_FALSE_RETURN(canUse());

	return ReleaseSemaphore(handle, 1, NULL) ? true : false;
}
bool Semaphore::open(const string& name)
{
	if (name.empty()) return false;

	HANDLE handle = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, FALSE, name.c_str());

	if (HandleCanUse(handle)) return open(handle);

	if (GetLastError() == ERROR_PATH_NOT_FOUND)
	{
		handle = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, FALSE, MD5GetEncodeString(name.c_str(), name.length(), TRUE).val);
		
		if (HandleCanUse(handle)) return open(handle);
	}

	return false;
}
bool Semaphore::create(const string& name, int size)
{
	if (name.empty()) return false;

	HANDLE handle = CreateSemaphoreA(NULL, 0, size, name.c_str());

	if (HandleCanUse(handle)) return open(handle);

	if (GetLastError() == ERROR_PATH_NOT_FOUND)
	{
		handle = CreateSemaphoreA(NULL, 0, size, MD5GetEncodeString(name.c_str(), name.length(), TRUE).val);
		
		if (HandleCanUse(handle)) return open(handle);
	}

	return false;
}
#endif

////////////////////////////////////////////////////////
#endif
